/**
 *
 * jcrt and its source-code is licensed under the LGPL.
 * You may copy, adapt, and redistribute this file for commercial or non-commercial use.
 * When copying, adapting, or redistributing this document in keeping with the guidelines above,
 * you are required to provide proper attribution to obinary.
 * If you reproduce or distribute the document without making any substantive modifications to its content,
 * please use the following attribution line:
 *
 * Copyright 2007 Atjava.com Ltd. (http://www.atjava.com) All rights reserved.
 *
 */
package com.atjava.jcrt.core.transaction;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;

import org.w3c.dom.Document;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;

import com.atjava.jcrt.core.SystemProperty;
/**
 * Xml Services Class
 * 
 * @author biggie
 * @version $Revision: 1.1 $ $Date: 2008/07/02 02:11:03 $
 */
public class XmlServices {

	private static XmlJcrtInfo jcrtInfo = null;

	/** XmlServices Instance */
	private static XmlServices services = null;

	/** Lock */
	private static Object initLock = new Object();

	private static String ClassName = "com.atjava.jcrt.core.transaction.XmlServices";

	/** configuration file */
	private String configurationfile = null;

	/**
	 * Get XmlServices Instance
	 * 
	 * @return
	 */
	public static XmlServices getInstance() throws XmlTransactionException {
		if (services == null) {
			synchronized (initLock) {
				try {
					Class c = Class.forName(ClassName);
					services = (XmlServices) c.newInstance();
				} catch (Exception e) {
					throw new XmlTransactionException(e);
				}
			}
		}
		return services;
	}

	private XmlServices() throws XmlTransactionException {
		if (configurationfile == null)
			configurationfile = SystemProperty.DEFAULT_JCRT_CFG;
	}

	/**
	 * set Configuration file
	 * 
	 * @param configurationfile
	 */
	public void setConfigurationfile(String configurationfile) {
		this.configurationfile = configurationfile;
	}

	/**
	 * Service Start
	 * 
	 * @throws XmlTransactionException
	 */
	public void startService() throws XmlTransactionException {
		try {
			Document doc = getDocument(configurationfile);
			NodeList rnList = doc.getChildNodes();
			Node repositoryNode = null;
			Node repositoryMapping = null;
			for(int i=0;i<rnList.getLength();i++){
				Node node = (Node)rnList.item(i);
				if(node.getNodeName().equals("jcrt-configuration")){
					NodeList jcList = node.getChildNodes();
					for(int j=0;j<jcList.getLength();j++){
						Node jcNode = (Node)jcList.item(j);
						if(jcNode.getNodeName().equals("Repository")){
							repositoryNode = jcNode;
						}
						if(jcNode.getNodeName().equals("RepositoryMapping")){
							repositoryMapping = jcNode;
						}
					}
					break;
				}
			}
			if (repositoryNode == null
					|| !repositoryNode.getNodeName().equals("Repository"))
				throw new XmlTransactionException(configurationfile
						+ " the first node("+repositoryNode+") is not start with <Repository>");
			
			if (repositoryMapping == null
					|| !repositoryMapping.getNodeName().equals(
							"RepositoryMapping"))
				throw new XmlTransactionException(
						configurationfile
								+ " the last node is not start with <RepositoryMapping>");
			jcrtInfo = new XmlJcrtInfo();

			setRepositoryInfo(jcrtInfo, repositoryNode);
			setRepositoryMappingInfo(jcrtInfo, repositoryMapping);

		} catch (XmlTransactionException e) {
			throw e;
		} catch (Exception e) {
			throw new XmlTransactionException("StartService is faild ! "
					+ e.getMessage());
		}
	}

	/**
	 * Set Repository Info With ConfigFile
	 * 
	 * @param info
	 * @param node
	 */
	private void setRepositoryInfo(XmlJcrtInfo info, Node node)
			throws XmlTransactionException {

		info.setRepositoryname(node.getAttributes().getNamedItem("name")
				.getNodeValue());
		info.setRepositoryprovider(node.getAttributes()
				.getNamedItem("provider").getNodeValue());
		info.setRepositoryloadOnStartup(node.getAttributes().getNamedItem(
				"loadOnStartup").getNodeValue());
		NodeList list = node.getChildNodes();
		Map<String, String> repositoryparams = new HashMap<String, String>();
		Map<String, List<String[]>> namespaces = new HashMap<String, List<String[]>>();
		List<String> workspaces = new ArrayList<String>();
		for (int i = 0; i < list.getLength(); i++) {
			Node nodeitem = list.item(i);
			if (nodeitem.getNodeName().equals("param")) {
				String name = nodeitem.getAttributes().getNamedItem("name")
						.getNodeValue();
				String value = nodeitem.getAttributes().getNamedItem("value")
						.getNodeValue();
				repositoryparams.put(name, value);
			}
			if (nodeitem.getNodeName().equals("workspace")) {
				String name = nodeitem.getAttributes().getNamedItem("name")
						.getNodeValue();
				workspaces.add(name);
				setNamespace(namespaces, name, nodeitem.getChildNodes());
			}
			if (nodeitem.getNodeName().equals("auth")) {
				String name = nodeitem.getAttributes().getNamedItem("name")
						.getNodeValue();
				String password = nodeitem.getAttributes().getNamedItem(
						"password").getNodeValue();
				info.setRepusername(name);
				info.setReppassword(password);
			}
		}
		info.setNamespaces(namespaces);
		info.setRepositoryparams(repositoryparams);
		info.setWorkspaces(workspaces);
	}

	/**
	 * Set namespace
	 * 
	 * @param namespaces
	 * @param workspace
	 * @param list
	 */
	private void setNamespace(Map<String, List<String[]>> namespaces,
			String workspace, NodeList list) throws XmlTransactionException {
		List<String[]> namespaceList = new ArrayList<String[]>();
		for (int i = 0; i < list.getLength(); i++) {
			Node nodeitem = list.item(i);
			if (nodeitem.getNodeName().equals("namespace")) {
				String prefix = nodeitem.getAttributes().getNamedItem("prefix")
						.getNodeValue();
				String uri = nodeitem.getAttributes().getNamedItem("uri")
						.getNodeValue();
				String[] pu = { prefix, uri };
				namespaceList.add(pu);
			}
		}
		namespaces.put(workspace, namespaceList);
	}

	/**
	 * Set RepositoryMapping Info With ConfigFile
	 * 
	 * @param info
	 * @param node
	 */
	private void setRepositoryMappingInfo(XmlJcrtInfo info, Node node)
			throws XmlTransactionException {
		List<XmlJcrtInfoDetail> repositoryMapping = new ArrayList<XmlJcrtInfoDetail>();
		NodeList list = node.getChildNodes();
		for (int i = 0; i < list.getLength(); i++) {
			Node nodeitem = list.item(i);
			if (nodeitem.getNodeName().equals("class")) {
				repositoryMapping.add(getJcrtDetail(nodeitem));
			}
		}
		info.setRepositoryMapping(repositoryMapping);
	}
	/**
	 * getJcrtDetail
	 * @param node
	 * @return
	 * @throws XmlTransactionException
	 */
	private XmlJcrtInfoDetail getJcrtDetail(Node node) throws XmlTransactionException{
		
		String classname = node.getAttributes().getNamedItem("name").getNodeValue();
		String repositoryname = node.getAttributes().getNamedItem("repository").getNodeValue();
		String workspacename = node.getAttributes().getNamedItem("workspace").getNodeValue();
		String namespace = node.getAttributes().getNamedItem("namespace").getNodeValue();

		XmlJcrtInfoDetail info = new XmlJcrtInfoDetail();
		info.setClassname(classname);
		info.setRepositoryname(repositoryname);
		info.setWorkspacename(workspacename);
		info.setNamespace(namespace);
		
		NodeList list = node.getChildNodes();
		Map<String,String> properties = new HashMap<String, String>();
		for (int i = 0; i < list.getLength(); i++) {
			Node nodeitem = list.item(i);
			if (nodeitem.getNodeName().equals("property")) {
				String name = nodeitem.getAttributes().getNamedItem("name").getNodeValue();
				String colum = nodeitem.getAttributes().getNamedItem("colum").getNodeValue();
				properties.put(name, colum);
			}
		}
		info.setProperties(properties);
		return info;
	}
	/**
	 * Get XmlJcrtInfo
	 * 
	 * @return
	 */
	public XmlJcrtInfo getXmlJcrtInfo() {
		return jcrtInfo;
	}

	/**
	 * Get Document
	 * 
	 * @param filename
	 * @return
	 */
	private Document getDocument(String filename)
			throws XmlTransactionException {
		Document document = null;
		try {
			DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
			DocumentBuilder builder = factory.newDocumentBuilder();
			document = builder.parse(filename);
			document.getDocumentElement().normalize();
		} catch (Exception e) {
			throw new XmlTransactionException(e);
		}
		return document;
	}
}
